﻿using System;
using System.Collections.Generic;
using System.Linq;
using DataModel.Managers.SubManage;
using DataModel.Models;

namespace DataModel.Managers
{
    public class UserManager
    {
        private readonly UniversityPortalEntities _entityModel;

        private Role _studentRole;
        private Role _professorRole;
        private Role _adminRole;

        public UserManager()
        {
            _entityModel = new UniversityPortalEntities();
        }


        private Guid CreateUser(string email, string lastName, string firstName, string familyName, Role role)
        {
            var user = new User
                {
                    ID = Guid.NewGuid(),
                    email = email,
                    familyName = familyName,
                    firstName = firstName,
                    lastName = lastName,
                    profileID = (new ProfileManager()).CreateProfile(null)
                };

            var passwordGenerator = new PasswordGenerator();
            var hasher = new Hasher();
            user.login = passwordGenerator.Generate();
            string password = passwordGenerator.Generate();
            user.password = hasher.GetMd5Hash(password);

            // sent e-mail with autogenerated login and password
            var mailSender = new MailSender();
            mailSender.SendLoginData(user.login, user.password, string.Format("{0} {1} {2}", lastName, firstName, familyName), email);

            if (role != null)
            {
                user.Roles.Add(role);
            }

            _entityModel.AddToUsers(user);
            _entityModel.SaveChanges();

            return user.ID;
        }

        public UserModel GetUser(Guid id)
        {
            User user = _entityModel.Users.FirstOrDefault(us => us.ID == id);

            return user != null ? new UserModel(user) : null;
        }

        public UserModel GetUser(string login)
        {
            User user = _entityModel.Users.FirstOrDefault(us => us.login == login);

            UserModel userModel = null;

            if (user != null)
            {
                userModel = new UserModel();
                FillUserProp(userModel, user.ID);
            }
            return userModel;
        }

        public Guid GetUserID(string login)
        {
            return _entityModel.Users.FirstOrDefault(user => user.login == login).ID;
        }

        public void FillUserProp(UserModel userModel, Guid userID)
        {
            User user = _entityModel.Users.FirstOrDefault(us => us.ID == userID);
            if (user != null)
            {
                userModel.ID = user.ID;
                userModel.Email = user.email;
                userModel.FamilyName = user.familyName;
                userModel.FirstName = user.firstName;
                userModel.LastName = user.lastName;
                userModel.Login = user.login;
                userModel.Password = user.password;
                var profileModel = new ProfileModel {Description = user.Profile.description};
                userModel.Profile = profileModel;
                var roles = user.Roles.Select(role => new RoleModel {Name = role.name}).ToList();
                userModel.Roles = roles;
            }
        }



        public void CreateStudent(string email, string lastName, string firstName, string familyName, Guid groupID)
        {
            if (_studentRole == null)
            {
                Role role = _entityModel.Roles.FirstOrDefault(rl => rl.name == "Student");
                if (role == null)
                {
                    (new RoleManager()).CreateRole("Student");
                }
                _studentRole = role;
            }

            var student = new Student
                {
                    userID = CreateUser(email, lastName, firstName, familyName, _studentRole),
                    ID = Guid.NewGuid(),
                    groupID = groupID
                };

            _entityModel.AddToStudents(student);
            _entityModel.SaveChanges();
        }

        public void CreateStudent(string email, string lastName, string firstName, string familyName, long groupNumber)
        {
            Group group = _entityModel.Groups.FirstOrDefault(gr => gr.number == groupNumber);
            if (group != null)
            {
                CreateStudent(email, lastName, firstName, familyName, group.ID);
            }
            else
            {
                throw new Exception(string.Format("Group {0} doesn't exist", group.ToString()));
            }
        }

        public StudentModel GetStudent(Guid userID)
        {
            var user = _entityModel.Users.FirstOrDefault(us => us.ID == userID);
            if (user.Roles.Any(rl => rl.name == "Student"))
            {
                var studentModel = new StudentModel();
                FillUserProp(studentModel, userID);

                Student student = _entityModel.Students.FirstOrDefault(st => st.userID == userID);
                if (student != null)
                {
                    studentModel.GroupNumber = student.Group.number;
                    return studentModel;
                }
            }
            return null;
        }

        public StudentModel GetStudent(string login)
        {
            User user = _entityModel.Users.FirstOrDefault(us => us.login == login);
            if (user != null)
            {
                return (GetStudent(user.ID));
            }
            return null;
        }

        public void CreateAdmin(string email, string lastName, string firstName, string familyName)
        {
            if (_adminRole == null)
            {
                Role role = _entityModel.Roles.FirstOrDefault(rl => rl.name == "Admin");
                if (role == null)
                {
                    (new RoleManager()).CreateRole("Admin");
                }
                _adminRole = role;
            }

            var admin = new Admin
                {
                    userID = CreateUser(email, lastName, firstName, familyName, _adminRole),
                    ID = Guid.NewGuid(),
                };

            _entityModel.AddToAdmins(admin);
            _entityModel.SaveChanges();
        }

        public AdminModel GetAdmin(Guid userID)
        {
            var user = _entityModel.Users.FirstOrDefault(us => us.ID == userID);
            if (user.Roles.Any(rl => rl.name == "Admin"))
            {
                var adminModel = new AdminModel();
                FillUserProp(adminModel, userID);
                return adminModel;
            }
            return null;
        }

        public AdminModel GetAdmin(string login)
        {
            User user = _entityModel.Users.FirstOrDefault(us => us.login == login);
            if (user != null)
            {
                return (GetAdmin(user.ID));
            }
            return null;
        }



        public void CreateProfessor(string email, string lastName, string firstName, string familyName, Guid degreeID, Guid chairID)
        {
            if (_professorRole == null)
            {
                Role role = _entityModel.Roles.FirstOrDefault(rl => rl.name == "Professor");
                if (role == null)
                {
                    (new RoleManager()).CreateRole("Professor");
                }
                _professorRole = role;
            }
            var professor = new Professor();
            professor.userID = CreateUser(email, lastName, firstName, familyName, _professorRole);
            professor.ID = Guid.NewGuid();
            professor.degreeID = degreeID;
            professor.chairID = chairID;

            _entityModel.AddToProfessors(professor);
            _entityModel.SaveChanges();
        }

        public void CreateProfessor(string email, string lastName, string firstName, string familyName, string degreeName, Guid chairID)
        {
            Degree degree = _entityModel.Degrees.FirstOrDefault(dg => dg.name == degreeName);
            if (degree != null)
            {
                CreateProfessor(email, lastName, firstName, familyName, degree.ID, chairID);
            }
            else
            {
                throw new Exception(string.Format("Degree {0} doesn't exist", degree));
            }
        }

        public void CreateProfessor(string email, string lastName, string firstName, string familyName, string degreeName, string chairShortName)
        {
            Degree degree = _entityModel.Degrees.FirstOrDefault(dg => dg.name == degreeName);
            Chair chair = _entityModel.Chairs.FirstOrDefault(ch => ch.shortName == chairShortName);
            if ((degree != null) && (chair != null))
            {
                CreateProfessor(email, lastName, firstName, familyName, degree.ID, chair.ID);
            }
            else
            {
                throw new Exception(string.Format("Degree {0} and Chair {1} doesn't exist", degree, chair));
            }
        }

        public void CreateProfessor(string email, string lastName, string firstName, string familyName, Guid degreeID, string chairShortName)
        {
            Chair chair = _entityModel.Chairs.FirstOrDefault(ch => ch.shortName == chairShortName);
            if (chair != null)
            {
                CreateProfessor(email, lastName, firstName, familyName, degreeID, chair.ID);
            }
            else
            {
                throw new Exception(string.Format("Chair {0} doesn't exist", chair));
            }
        }

        public ProfessorModel GetProfessor(Professor professor)
        {
            var professorModel = new ProfessorModel();


            professorModel.Email = professor.User.email;
            professorModel.FamilyName = professor.User.familyName;
            professorModel.FirstName = professor.User.firstName;
            professorModel.LastName = professor.User.lastName;
            professorModel.Login = professor.User.login;
            professorModel.Password = professor.User.password;
            var profileModel = new ProfileModel();
            profileModel.Description = professor.User.Profile.description;
            professorModel.Profile = profileModel;
            foreach (var role in professor.User.Roles)
            {
                var roleModel = new RoleModel();
                roleModel.Name = role.name;
                professorModel.Roles.Add(roleModel);
            }
            return null;
        }

        public ProfessorModel GetProfessor(Guid userID)
        {
            var user = _entityModel.Users.FirstOrDefault(us => us.ID == userID);
            if (user.Roles.Any(rl => rl.name == "Professor"))
            {
                var profModel = new ProfessorModel();
                FillUserProp(profModel, userID);

                var professor = _entityModel.Professors.FirstOrDefault(pr => pr.userID == user.ID);
                if (professor != null)
                {

                }
            }
            return null;
        }

        public ProfessorModel GetProfessor(string login)
        {
            User user = _entityModel.Users.FirstOrDefault(us => us.login == login);
            if (user != null)
            {
                return (GetProfessor(user.ID));
            }
            return null;
        }

        public bool ChangeUserLogin(Guid userID, string newLogin)
        {
            User user = _entityModel.Users.FirstOrDefault(u => u.ID == userID);

            if (user == null) return false;
            user.login = newLogin;
            _entityModel.SaveChanges();

            return true;
        }

        public bool ChangeUserPassword(Guid userID, string oldPassword, string newPassword)
        {
            Hasher hasher = new Hasher();
            User user = _entityModel.Users.FirstOrDefault(u => u.ID == userID);

            if (user == null || !hasher.VerifyMd5Hash(oldPassword, user.password))
            {
                return false;
            }
            user.password = hasher.GetMd5Hash(newPassword);
            _entityModel.SaveChanges();

            return true;
        }

        public bool DeleteUserRole(Guid userID, Guid roleID)
        {
            User user = _entityModel.Users.FirstOrDefault(u => u.ID == userID);
            Role role = user.Roles.FirstOrDefault(r => r.ID == roleID);
            if (role != null)
            {
                user.Roles.Remove(role);
                _entityModel.SaveChanges();
                return true;
            }
            return false;
        }

        public bool AddRoleToUser(Guid userID, Guid roleID)
        {
            User user = _entityModel.Users.FirstOrDefault(u => u.ID == userID);
            if (user.Roles.FirstOrDefault(r => r.ID == roleID) == null)
            {
                user.Roles.Add(_entityModel.Roles.FirstOrDefault(r => r.ID == roleID));
                _entityModel.SaveChanges();
                return true;
            }
            return false;
        }

        public List<UserModel> GetAllUsers()
        {
            List<UserModel> userModels = new List<UserModel>();
            foreach (var user in _entityModel.Users)
            {
                userModels.Add(new UserModel(user));
            }

            return userModels;
        }

        public long GetStudentGroup(string login)
        {
            User user = _entityModel.Users.FirstOrDefault(us => us.login == login);
            if (user != null)
            {
                Student student = _entityModel.Students.FirstOrDefault(st => st.userID == user.ID);
                return student.Group.number;
            }
            return 0;
        }

        public IEnumerable<ProfessorModel> GetAllProfessors()
        {
            var professors = new List<ProfessorModel>();
            if (_entityModel.Professors == null) { return null; }
            professors = _entityModel.Professors.Select(p => new ProfessorModel()
                {
                    ID = p.ID,
                    FirstName = p.User.firstName,
                    FamilyName = p.User.familyName,
                    LastName = p.User.lastName,
                    Degree = p.Degree.name,
                    chair = new ChairModel()
                    {
                        FullName = p.Chair.fullName,
                        ShortName = p.Chair.shortName,
                        Description = p.Chair.description
                    },

                }).ToList();
            return professors;
        }
    }
}
